
local protobuf = require "protobuf"

local ByteArray = require "quick.cc.utils.ByteArray"

--[[
    Packet Struct : see message.proto
--]]

local Packet = class("Packet")

local protobuf_encode_func = protobuf.encode
local protobuf_decode_func = protobuf.decode
local ba_readByte = ByteArray.readByte
local ba_readBuf = ByteArray.readBuf
local ba_writeBuf = ByteArray.writeBuf
local ba_setPos = ByteArray.setPos
local ba_getLen = ByteArray.getLen

local string_isStartWith = string.isStartWith

Packet.CompressRoute = true

local pool = {}
pool[0]=0

function Packet.fetch()
    local len = pool[0]
    if len > 0 then
        local obj = pool[len]
        pool[len] = nil
        pool[0] = len - 1
        return obj
    else
        return Packet:new()
    end
end

function Packet.free(obj)
    local len = pool[0] + 1
    pool[len] = obj
    pool[0] = len
end

function Packet.msg2enum(msg)
    local dotpos = string.find(msg,'%.')
    local package = string.sub(msg,1,dotpos-1)
    local message = string.sub(msg,dotpos+1)
    local enumStr = package.."_"..message
    return protobuf.enum_id("dict.EDict",enumStr)
end

function Packet.enum2msg(enum)
    local enumStr = protobuf.enum_name("dict.EDict",enum)
    if not enumStr then 
        return nil
    end
    local isPb = string_isStartWith(enumStr, "pb_")
    local startPos = isPb and 4 or 1
    local splitpos = string.find(enumStr, "_", startPos)
    local package  = string.sub(enumStr,1,splitpos-1)
    local msg = string.sub(enumStr,splitpos+1)
    return package.."."..msg
end

function Packet.create(message,data,cb)
    local p = Packet.fetch()
    p.id = 0
    p.type = message
    p.data = data
    p.command = 0
    p.callbackFun = cb
    return p
end

function Packet.createFromBinary(msgId,data)
    local p = Packet.fetch()
    local rawData = p:decode(msgId,data)
    return p,rawData
end

function Packet:ctor()
    self.id = 0
    self.type = nil
    self.data = nil
    self.command = 0
    self.callbackFun = nil
end


function Packet:invoke(data,err)
    if self.callbackFun then
        return self.callbackFun(data,err)
    end
end

function Packet:encode()
    local msgId = assert(Packet.msg2enum(self.type),self.type)
    self.command = msgId
    local encodeData = protobuf_encode_func(self.type, self.data)
    return SerializeHelper.encode(self.id,encodeData)
end

function Packet:decode(msgId,data)
    local sequence,bodybuf = SerializeHelper.decode(data)
    self.id = sequence
    local msg
    if type(msgId)=="number" and msgId>0 then
        msg = assert(Packet.enum2msg(msgId),"Packet:decode msgId:"..msgId.." not in dict .")
    end
    self.type = msg
    self.command = msgId
    local err = nil
    self.data,err = protobuf_decode_func(msg,bodybuf)
    assert(self.data,err)
    
    --if DEBUG>0 then
        protobuf.extract(self.data)
    --end

    return bodybuf
end

return Packet